/* 链表类 */
class LinkedList {
    /**
     * 构造函数
     * @items	 要初始化的多个项目
     */
    constructor(...items) {
        /* 内部属性 */
        // 长度
        this.length = 0;
        // 头部
        this.head = null;
        // 初始化内容
        this.append(...items);
    }

    /* 原型属性 */

    /* 设置类型 */
    get [Symbol.toStringTag]() {
        return "LinkedList";
     }

    /* 获取节点 */
    get Node() {
        // 返回节点类
        return class {
            /**
             * 生成节点
             * @item	当前节点存放的数据
             */
            constructor(item) {
                // 当前节点的数据
                this.item = item;
                // 指向下一个节点的指针
                this.next = null;
            }
        }
    }

    /* 原型方法 */

    // [Symbol.iterator] 该方法用于实现 for...of 遍历与扩展运算符(...)
    [Symbol.iterator]() {
        // 声明指针
        let pointer = this.head,
            // 索引值
            index = 0;
        
        // 返回值
        return {
            // next 方法
            next:() => {

                // 声明结果变量并获取是否已经遍历完成
                let result = { done: (++index >= this.length)};
                // 判断是否已经遍历完成
                if(!this.done) {
                    // 设置 value 值
                    result.value = pointer.item;
                    // 更新指针
                    pointer = pointer.next;
                }
                // 返回结果变量
                return result;
            }
        }
    }

    /**
     * 添加至末尾
     * @items	 要追加的多个项目
     * return	 undefined
     */
    append(...items) {

        // 参数为空则直接退出
        if (!items.length) return;

        // 备份参数列表长度
        let length = items.length;

        // 声明索引值
        let _index = 0,
            // 指针
            pointer = this.head;

        // 判断链表长度
        switch (this.length) {
            case 0:
                // 获取第一个项目，使用 shift，避免后续重复添加
                let item = items.shift();
                // 生成节点
                let node = new this.Node(item);
                // 更新 head
                this.head = node;
            case 1:
                // 更新指针
                pointer = this.head;
                // 退出 switch
                break;
            // 其他情况
            default:
                // 只要计数索引值未达到总长度就继续遍历
                while (_index++ < this.length - 1) {
                    // 更新指针
                    pointer = pointer.next;
                }
                // 退出 switch
                break;
        }


        // 遍历参数集合
        for (let item of items) {
            // 生成节点
            let node = new this.Node(item);
            // 追加节点
            pointer.next = node;
            // 更新指针
            pointer = pointer.next;
        }
        // 更新长度
        this.length += length;
    }

    /**
     * 获取指定位置内容
     * @index 指定位置对应的索引值
     * return 返回对应的节点内容
     */
    get(index, isNode = false) {
        /* 判断链表是否为空 */
        if (!this.length) return;

        /* 索引值修正 */
        // 如果大于最大值设为最大值
        index > this.length - 1 && (index = this.length - 1);
        // 如果小于 0 设为 0
        index < 0 && (index = 0);

        // 声明指针
        let pointer = this.head;
        // 声明索引值
        let _index = 0;

        // 遍历更新指针直至最大长度
        while (_index++ < index) {
            // 更新指针
            pointer = pointer.next;
        }

        // 返回节点内容
        return isNode ? pointer : pointer.item;
    }

    /**
     * 添加至指定位置
     * @index	 插入至 index 前
     * @item		要插入的项目
     * return	 undefined
     */
    insert(...args) {

        // 参数解构
        let [index, item] = args;

        // 判断参数合法性
        if (!(typeof index === 'number' && args.length > 1)) return;

        // 生成新节点
        let node = new this.Node(item);

        // 声明指针
        let pointer;

        // 判断 index 是否为 0
        if (!index) {
            // 将指针指向 head
            pointer = this.head;
            // 将新节点设置 head
            this.head = node;
            // 将新节点的 next 指向指针所在节点
            this.head.next = pointer;
        } else {
            // 获取索引值对应节点的上一个节点
            let previous = this.get(index - 1, true);

            // 检测值合法性，非法直接退出
            if (!previous) return;

            // 声明指针，指向索引值对应的节点
            pointer = previous.next;

            // 将上一个节点指向新节点
            previous.next = node;

            // 将新节点指向索引对应的节点
            node.next = pointer;
        }

        // 更新长度
        this.length++;
    }

    /**
     * 根据索引删除节点
     * @index 要删除节点的索引值
     */
    removeIndex(index) {

        /* 判断链表是否为空 */
        if (!this.length) return;

        /* 合法性检测，非法直接退出 */
        if (typeof index != 'number' || index < 0 || index > this.length - 1) return;

        // 定义指针
        let pointer;

        // 判断索引值是否为 0
        if (!index) {
            // 更新指针
            pointer = this.head;
            // 判断长度是否为 1
            if (this.length === 1) {
                // 将 head 指向 null
                this.head = null;
            } else {
                // 将 head 指向下一个节点
                this.head = this.head.next;
            }
        } else {
            // 获取指定索引值的上一个节点
            let previous = this.get(index - 1, true);

            // 检测值合法性，非法直接退出
            if (!previous) return;

            // 更新指针
            pointer = previous.next;

            // 使该节点指向 null
            previous.next = pointer.next;

        }

        // 更新长度
        this.length--;

        // 返回指针所在节点
        return pointer.item;

    }

    /** 
     * 根据内容删除节点
     * @item 要删除节点的内容
     */
    removeItem(...args) {
        // 获取内容对应的索引值
        let index = this.find(...args);
        // 删除索引值对应的数据
        this.removeIndex(index);
        // 返回获取的索引值
        return index;
    }

    /**
     * 根据索引或内容删除节点
     * @index | item 要删除节点的索引或内容
     * @isItem		 设置是否根据内容进行删除
     */
    remove(...args) {
        // 判断处理类型
        if (args[1]) {
            // 根据内容删除
            this.removeItem(args[0]);
        } else {
            // 根据索引删除
            this.removeIndex(args[0]);
        }
    }

    /**
     * 删除末尾节点方法
     * return 删除的节点
     */
    delete() {
        // 删除最后一个节点并返回
        return this.removeIndex(this.length - 1);
    }

    /**
     * 设置节点内容
     * @index 要设置内容的节点
     * @item	要设置的内容
     */
    set(index, item) {

        // 获取索引值对应节点
        let pointer = this.get(index, true);

        // 检测值合法性，非法直接退出
        if (!pointer) return;

        // 声明变量存储原数据
        let oldItem = pointer.item;

        // 更新数据
        pointer.item = item;

        // 返回原数据
        return oldItem;
    }

    /**
     * 查找节点位置
     * @item	要查找节点的内容
     * return 要查找节点的索引
     */
    find(...args) {
        // 参数为空则直接退出
        if (!args.length) return;

        // 解构参数
        let [item] = args;

        // 声明指针
        let pointer = this.head;
        // 声明索引值
        let _index = 0;

        // 遍历更新指针直至最大长度
        while (_index++ < this.length) {
            // 如果当前节点内容与查找的节点内容相同
            if (pointer.item === item) {
                // 返回节点对应索引
                return _index - 1;
            }
            // 更新指针
            pointer = pointer.next;
        }
    }

    /**
     * 拼接多个链表
     * @newLinkedLists	 要拼接的多个链表
     * return			 拼接后的链表
     */
    concat(...newLinkedLists) {

        // 参数为空则直接退出
        if (!newLinkedLists.length) return this;

        // 遍历参数集合
        for (let newLinkedList of newLinkedLists) {

            // 判断自身是否为空链表
            if (this.length === 0) {
                // 使用目标链表的 head 作为自身的 head 
                this.head = newLinkedList.head;
            } else {
                // 声明指针获取自身最后一个节点
                let pointer = this.get(this.length - 1, true);
                // 将指针所在节点的 next 指向目标节点的 head
                pointer.next = newLinkedList.head;
            }

            // 更新长度
            this.length += newLinkedList.length;
        }
    }

    /**
     * 拼接为字符串
     */
    join(connector = '') {

        // 长度为空则直接退出
        if (!this.length) return;

        // 声明结果变量
        let result = '',
            // 索引值
            _index = 0,
            // 指针
            pointer = this.head;

        // 遍历至最大索引值减 1
        while (_index++ < this.length - 1) {
            // 字符拼接
            result += pointer.item + connector;

            // 更新指针
            pointer = pointer.next;
        }

        // 完成最后的拼接
        result += pointer.item;

        // 返回结果变量
        return result;
    }

    /**
     * 清空
     */
    clear() {
        // 将 head 指向 null 以清除引用
        this.head = null;
        // 重置 length 属性
        this.length = 0;
    }
}